#include <prism/frontend/lexical_analysis/lex.hpp>
//=====================================================================
#include <iostream>
//=====================================================================
#include <sooty/frontend/lexical_analysis/lexer.hpp>
#include <sooty/frontend/lexical_analysis/lexemifier.hpp>
//=====================================================================
using namespace sooty;
using namespace prism;

//=====================================================================
// local variables
//=====================================================================
lexemifier_t lexemifier; 

int previous_indent = 0;
int current_indent = 0;



void newline_action(lex_results_t& R)
{
	if (lexemifier.list.size() == 0) return;
	
	// remove the previous dents if the physical line was empty
	lexeme_list::const_iterator last_newline = lexemifier.list.end(channels::newline);
	lexeme_list::const_iterator last_main = lexemifier.list.end(channels::main);
	
	if (last_newline->position.stream > last_main->position.stream)
		lexemifier.list.erase(last_newline, lexemifier.list.end());
	else
		previous_indent = current_indent;
	
	current_indent = 0;
	
	if (R.start != R.stop) {
		lexemifier.make(lexid::newline, channels::newline)(R);
	}
	
	lexemifier.newliner()(R);
}

void dent_action(lex_results_t& R)
{
	if (current_indent < previous_indent)
		for (int i = current_indent; i < previous_indent; ++i)
			lexemifier.make(lexid::dedent, channels::scoper)(R);
	else if (current_indent > previous_indent)
		for (int i = current_indent; i > previous_indent; --i)
			lexemifier.make(lexid::indent, channels::scoper)(R);
}

void tab_action(lex_results_t& R) {
	++current_indent;
}

lexer tab =
	char_('\t')[ tab_action ]
	;

lexer newline =
	(string_("\r\n") | char_('\n'))[newline_action] >> (*tab)[dent_action]
	;

lexer eof =
	eof_()[newline_action] >> eof_()[dent_action] >> any(); // we want any to fail
	;

lexer whitespace =
	+(any_of(" \f") | newline)
	;

lexer identifier =
	   ((char_('_') | in_range('a', 'z') | in_range('A', 'Z'))
	>> *(char_('_') | in_range('a', 'z') | in_range('A', 'Z') | in_range('0', '9')))
		[ lexemifier.make(lexid::identifier, channels::main) ]
	;
	
lexer int_literal =
	(!char_('-') >> +in_range('0', '9'))[ lexemifier.make<int>(lexid::int_literal, channels::main) ]
	;

lexer real_literal =
	(!char_('-') >> +in_range('0', '9') >> char_('.') >> +in_range('0', '9'))[ lexemifier.make<float>(lexid::real_literal, channels::main) ]
	;

lexer keywords =
	  string_("int")      [ lexemifier.make(lexid::int_keyword, channels::main, false) ]
	| string_("real")     [ lexemifier.make(lexid::real_keyword, channels::main, false) ]
	| string_("bool")     [ lexemifier.make(lexid::bool_keyword, channels::main, false) ]
	| string_("function") [ lexemifier.make(lexid::function_keyword, channels::main, false) ]
	| string_("return")   [ lexemifier.make(lexid::return_keyword, channels::main, false) ]
	| string_("pass")     [ lexemifier.make(lexid::pass_keyword, channels::main, false) ]
	| string_("typeof")   [ lexemifier.make(lexid::typeof_keyword, channels::main, false) ]
	| string_("as")       [ lexemifier.make(lexid::as_keyword, channels::main, false) ]
	| string_("new")      [ lexemifier.make(lexid::new_keyword, channels::main, false) ]
	| string_("delete")   [ lexemifier.make(lexid::delete_keyword, channels::main, false) ]
	| string_("if")       [ lexemifier.make(lexid::if_keyword, channels::main, false) ]
	| string_("else")     [ lexemifier.make(lexid::else_keyword, channels::main, false) ]
	| string_("true")     [ lexemifier.make(lexid::bool_literal, channels::main, true) ]
	| string_("false")    [ lexemifier.make(lexid::bool_literal, channels::main, true) ]
	;

lexer symbols =
	  char_('(')[ lexemifier.make(lexid::lparen, channels::main, false) ]
	| char_(')')[ lexemifier.make(lexid::rparen, channels::main, false) ]
	| char_('{')[ lexemifier.make(lexid::lcurly, channels::main, false) ]
	| char_('}')[ lexemifier.make(lexid::rcurly, channels::main, false) ]
	| char_(',')[ lexemifier.make(lexid::comma, channels::main, false)  ]
	| char_(';')[ lexemifier.make(lexid::semi, channels::main, false)   ]
	| char_(':')[ lexemifier.make(lexid::colon, channels::main, false)  ]
	| char_('+')[ lexemifier.make(lexid::plus, channels::main, false)   ]
	| char_('-')[ lexemifier.make(lexid::dash, channels::main, false)   ]
	| char_('*')[ lexemifier.make(lexid::star, channels::main, false)   ]
	| char_('/')[ lexemifier.make(lexid::fwdslash, channels::main, false)   ]
	| string_("==") [ lexemifier.make(lexid::equality, channels::main, false) ]
	| char_('=')[ lexemifier.make(lexid::equals, channels::main, false) ]
	| char_('<')[ lexemifier.make(lexid::ltri, channels::main, false) ]
	| char_('>')[ lexemifier.make(lexid::rtri, channels::main, false) ]
	| char_('&')[ lexemifier.make(lexid::ampersand, channels::main, false) ]
	;

lexer start =
	(*(whitespace | real_literal | int_literal | keywords | identifier | symbols | eof))
	;

sooty::lexeme_list prism::lex( const sooty::input_iterator& begin, const sooty::input_iterator& end )
{
	//std::cout << std::endl;
	lexemifier.list = lexeme_list();
	start(begin, end);
	return lexemifier.list;
}